<!DOCTYPE html>
<html lang="en">
<head>
    <title>moxxmpp: A Minimal Working Example (in Dart)</title>
    <link href="style.css" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/bash.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/dart.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/yaml.min.js"></script>
    <script>hljs.highlightAll();</script>
</head>
<body>

<article>

    <header>
        <img src="images/header_logo.gif" alt="Openfire Logo" />
        <h1>moxxmpp: A Minimal Working Example (in Dart)</h1>
    </header>

    <nav>
        <a href="index.html">&laquo; Back to documentation index</a>
    </nav>

    <section id="intro">

        <h2>Introduction</h2>

        <p>
            This document provides a minimal working example of a client implementation using the moxxmpp library, making
            it connect to a running Openfire server.
        </p>

        <p>Topics that are covered in this document:</p>

        <nav>
            <ul>
                <li><a href="#background">Background</a>
                <li><a href="#preparations">Preparations</a>
                <li><a href="#code">Code</a>
                <li><a href="#references">Further Reading</a>
            </ul>
        </nav>

    </section>

    <section id="background">

        <h2>Background</h2>

        <p>
            <a href="https://codeberg.org/moxxy/moxxmpp">moxxmpp</a> is a pure Dart library that was originally built for
            the Moxxy XMPP client. It has since been separated and can be used standalone.
        </p>
        <p>
            This guide describes how to use moxxmpp to connect to Openfire. It provides nothing more than a minimal
            working example, intended as a stepping stone to for client developers that get started with a new project.
        </p>

    </section>

    <section id="preparations">

        <h2>Preparations</h2>

        <p>
            In this example, a client connection will be made against a running Openfire server. For ease of
            configuration, the 'demoboot' setup of Openfire is used.
        </p>
        <p>
            The 'demoboot' setup of Openfire allows one to start a fresh installation of Openfire into a certain
            provisioned state, without running any of the setup steps. When running in 'demoboot' mode:
        </p>
        <ul>
            <li>an administrative account is created using the username 'admin' and password 'admin'</li>
            <li>two users are automatically created: 'jane' and 'john' (both using the value 'secret' as their password)</li>
            <li>the XMPP domain name is configured to be 'example.org' (for ease of use, configure 'example.org' to be an alias of '127.0.0.1' in your hosts file!)</li>
        </ul>
        <p>
            To start Openfire in 'demoboot' mode, you can invoke the Openfire executable using the <code>-demoboot</code>
            argument, as shown below.
        </p>
        <fieldset>
            <legend>Starting Openfire in 'demoboot' mode.</legend>
            <pre><code>$ ./bin/openfire.sh -demoboot</code></pre>
        </fieldset>

    </section>

    <section id="code">

        <h2>Code</h2>

        <p>
            To start the project, create a file named <code>pubspec.yaml</code> in an empty directory, and copy in the
            code below.
        </p>

        <fieldset>
            <legend>Example pubspec.yaml file</legend>
            <pre><code class="language-yaml">name: example
description: A moxxmpp sample for Openfire.
version: 1.0.0

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  logging: ^1.0.2
  moxxmpp:
    hosted: https://git.polynom.me/api/packages/Moxxy/pub
    version: 0.4.0
  moxxmpp_socket_tcp:
    hosted: https://git.polynom.me/api/packages/Moxxy/pub
    version: 0.4.0
</code></pre>
        </fieldset>

        <p>
            Next, create a file named <code>main.dart</code> in an empty directory, and copy in the code
            below.
        </p>

        <fieldset>
            <legend>Example main.dart file</legend>
            <pre><code class="language-dart">import 'package:logging/logging.dart';
import 'package:moxxmpp/moxxmpp.dart';
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';

/// By default, moxxmpp expects the server to present a valid TLS certificate that
/// the system trusts. In this case, however, the certificate is self-signed, meaning
/// that the system does not trust it. To work around this issue, we create a sub-class
/// of the [TCPSocketWrapper] that bypasses the validity check and accepts any TLS
/// certificate that the server gives us. In production, you absolutely do not want to
/// do this.
class SelfSignedTCPSocketWrapper extends TCPSocketWrapper {
  SelfSignedTCPSocketWrapper() : super(false);

  @override
  bool onBadCertificate(dynamic certificate, String domain) {
    return true;
  }
}

/// The JID we want to authenticate as.
final xmppUser = JID.fromString('jane@example.com');

/// The password to authenticate with.
const xmppPass = 'secret';

/// The [xmppHost]:[xmppPort] server address to connect to.
/// In a real application, one might prefer to use [TCPSocketWrapper]
/// with a custom DNS implementation to let moxxmpp resolve the XMPP
/// server's address automatically. However, if we just provide a host
/// and a port, then [TCPSocketWrapper] will just skip the resolution and
/// immediately use the provided connection details.
const xmppHost = 'localhost';
const xmppPort = 5222;

void main(List args) async {
  Logger.root.level = Level.ALL;
  Logger.root.onRecord.listen((record) {
    print('${record.level.name}|${record.time}: ${record.message}');
  });

  // This class manages every aspect of handling the XMPP stream.
  final connection = XmppConnection(
    // A reconnection policy tells the connection how to handle an error
    // while or after connecting to the server. The [TestingReconnectionPolicy]
    // immediately triggers a reconnection. In a real implementation, one might
    // prefer to use a smarter strategy, like using an exponential backoff.
    TestingReconnectionPolicy(),

    // A connectivity manager tells the connection when it can connect. This is to
    // ensure that we're not constantly trying to reconnect because we have no
    // Internet connection. [AlwaysConnectedConnectivityManager] always says that
    // we're connected. In a real application, one might prefer to use a smarter
    // strategy, like using connectivity_plus to query the system's network connectivity
    // state.
    AlwaysConnectedConnectivityManager(),

    // This kind of negotiator tells the connection how to handle the stream
    // negotiations. The [ClientToServerNegotiator] allows to connect to the server
    // as a regular client. Another negotiator would be the [ComponentToServerNegotiator] that
    // allows for connections to the server where we're acting as a component.
    ClientToServerNegotiator(),

    // A wrapper around any kind of connection. In this case, we use the [SelfSignedTCPSocketWrapper]. It wraps
    // [TCPSocketWrapper], which uses a dart:io Socket/SecureSocket to connect to the server. If you want, you can also
    // provide your own socket to use, for example, WebSockets or any other connection
    // mechanism.
    SelfSignedTCPSocketWrapper(),
  )..connectionSettings = ConnectionSettings(
      jid: xmppUser,
      password: xmppPass,
      host: xmppHost,
      port: xmppPort,
    );

  // Register a set of "managers" that provide you with implementations of various
  // XEPs. Some have interdependencies, which need to be met. However, this example keeps
  // it simple and just registers a [MessageManager], which has no required dependencies.
  await connection.registerManagers([
    // The [MessageManager] handles receiving and sending  stanzas.
    MessageManager(),
  ]);

  // Feature negotiators are objects that tell the connection negotiator what stream features
  // we can negotiate and enable. moxxmpp negotiators always try to enable their features.
  await connection.registerFeatureNegotiators([
    // This negotiator authenticates to the server using SASL PLAIN with the provided
    // credentials.
    SaslPlainNegotiator(),
    // This negotiator attempts to bind a resource. By default, it's always a random one.
    ResourceBindingNegotiator(),
    // This negotiator attempts to do StartTLS before authenticating.
    StartTlsNegotiator(),
  ]);

  // Set up a stream handler for the connection's event stream. Managers and negotiators
  // may trigger certain events. The [MessageManager], for example, triggers a [MessageEvent]
  // whenever a message is received. If other managers are registered that parse a message's
  // contents, then they can add their data to the event.
  connection.asBroadcastStream().listen((event) {
    if (event is! MessageEvent) {
      return;
    }

    // The text body (contents of the  element) are returned as a
    // [MessageBodyData] object. However, a message does not have to contain a
    // body, so it is nullable.
    final body = event.extensions.get()?.body;
    print('[<-- ${event.from}] $body');
  });

  // Connect to the server.
  final result = await connection.connect(
    // This flag indicates that we want to reconnect in case something happens.
    shouldReconnect: true,
    // This flag indicates that we want the returned Future to only resolve
    // once the stream negotiations are done and no negotiator has any feature left
    // to negotiate.
    waitUntilLogin: true,
  );

  // Check if the connection was successful. [connection.connect] can return a boolean
  // to indicate success or a [XmppError] in case the connection attempt failed.
  if (!result.isType()) {
    print('Failed to connect to server');
    return;
  }
}</code></pre>
        </fieldset>

        <p>
            Finally, build and run the test client, using the instructions below.
        </p>

        <fieldset>
            <legend>Build and run moxxmpp test client</legend>
            <pre><code class="language-bash">$ dart pub get
$ dart run main.dart</code></pre>
        </fieldset>

        <p>
            If all goes well, this will print a short exchange of XMPP data.
        </p>

        <p>
            Note that this example disables important security features. You should not use this for anything important!
        </p>

    </section>

    <section id="references">

        <h2>Further Reading</h2>

        <p>
            Please use the links below to find more information.
        </p>
        <ul>
            <li><a href="https://codeberg.org/moxxy/moxxmpp">moxxmpp project home page</a></li>
        </ul>
    </section>

    <footer>
        <p>
            An active support community for Openfire is available at
            <a href="https://discourse.igniterealtime.org">https://discourse.igniterealtime.org</a>.
        </p>
    </footer>

</article>

</body>
</html>
